package com.why.zing.payapi.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.why.zing.alipay.builder.AlipayTradeQueryRequestBuilder;
import com.why.zing.alipay.result.AlipayF2FQueryResult;
import com.why.zing.alipay.service.AlipayTradeService;
import com.why.zing.common.domain.ZingResult;
import com.why.zing.common.utils.DateUtil;
import com.why.zing.payapi.constant.AliPayStatusEnum;
import com.why.zing.payapi.constant.Constants;
import com.why.zing.payapi.constant.OrderStatusEnum;
import com.why.zing.payapi.constant.PayCodeEnum;
import com.why.zing.payapi.dao.PayOrderMapper;
import com.why.zing.payapi.entity.PayOrder;
import com.why.zing.payapi.model.AliPayCallBackRequest;
import com.why.zing.payapi.model.ClosePayOrderRequest;
import com.why.zing.payapi.model.OrderNotify;
import com.why.zing.payapi.service.CallBackService;
import com.why.zing.payapi.service.JobService;
import com.why.zing.payapi.service.PayOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.why.zing.payapi.constant.AliPayStatusEnum.TRADE_FINISHED;
import static com.why.zing.payapi.constant.AliPayStatusEnum.getEnumByCode;

/**
 * 接收通知的接口信息
 *
 * @author 睁眼看世界
 * @date 2020年1月22日
 */
@Slf4j
@Service
public class CallBackServiceImpl implements CallBackService {

    @Value("${pay.alibaba.alipayPublicKey}")
    private String aliPublicKey;

    @Value("${pay.alibaba.signType}")
    private String signType;

    @Autowired
    private PayOrderService payOrderService;

    @Autowired
    private JobService jobService;

    @Autowired
    private AlipayTradeService alipayTradeService;

    /**
     * 支付宝通知结果处理
     */
    @Override
    public String aliCallBack(Map<String, String> aliPayCallBackMap) {
        if (!checkAlipaySign(aliPayCallBackMap)) {
            log.error("aliCallBack signCheck error {}", PayCodeEnum.ALI_SIGN_ERROR.getInfo());
            return Constants.ALI_CALLBACK_FAIL;
        }

        AliPayCallBackRequest aliPayCallBackRequest = JSON.parseObject(JSON.toJSONString(aliPayCallBackMap), AliPayCallBackRequest.class);
        PayOrder payOrder = new PayOrder();
        payOrder.setOrderNo(aliPayCallBackRequest.getOut_trade_no());
        List<PayOrder> payOrderList = payOrderService.select(payOrder);
        if (CollectionUtils.isEmpty(payOrderList)) {
            return Constants.ALI_CALLBACK_FAIL;
        }
        payOrder = payOrderList.get(0);
        if (StringUtils.equals(payOrder.getStatus(), OrderStatusEnum.FINISH.getCode())) {
            return Constants.ALI_CALLBACK_SUCCESS;
        }

        if (payOrder.getPayAmount().compareTo(aliPayCallBackRequest.getTotal_amount()) != 0) {
            return Constants.ALI_CALLBACK_FAIL;
        }

        AliPayStatusEnum aliPayStatusEnum = getEnumByCode(aliPayCallBackRequest.getTrade_status());

        PayOrder payOrderCallBack = new PayOrder();
        payOrderCallBack.setId(payOrder.getId());
        switch (aliPayStatusEnum) {
            case WAIT_BUYER_PAY:
                payOrderCallBack.setStatus(OrderStatusEnum.WAIT_PAY.getCode());
                break;
            case TRADE_CLOSED:
                payOrderCallBack.setStatus(OrderStatusEnum.CANCEL_FAILD.getCode());
                OrderNotify orderNotifyFailed = assemblyOrderNotify(payOrder, payOrderCallBack);
                jobService.addNotifyOrderJob(payOrder.getOrderNo(), payOrder.getCallbackUrl(), JSON.toJSONString(orderNotifyFailed));
                break;
            case TRADE_SUCCESS:
            case TRADE_FINISHED:
                payOrderCallBack.setStatus(OrderStatusEnum.FINISH.getCode());
                payOrderCallBack.setTradeTime(new Date());
                payOrderCallBack.setOutOrderNo(aliPayCallBackRequest.getTrade_no());
                OrderNotify orderNotifySuccess = assemblyOrderNotify(payOrder, payOrderCallBack);
                jobService.addNotifyOrderJob(payOrder.getOrderNo(), payOrder.getCallbackUrl(), JSON.toJSONString(orderNotifySuccess));
                break;
            default:
                payOrderCallBack.setStatus(OrderStatusEnum.CREATE.getCode());
        }

        payOrderService.updatePayOrder(payOrderCallBack);
        return Constants.ALI_CALLBACK_SUCCESS;
    }

    private boolean checkAlipaySign(Map<String, String> aliPayCallBackMap) {
        boolean rsaCheckV2 = Boolean.FALSE;
        try {
            return AlipaySignature.rsaCheckV1(aliPayCallBackMap, aliPublicKey, "UTF-8", signType);
        } catch (AlipayApiException e) {
            log.error("checkAlipaySign error", e);
        }
        return rsaCheckV2;
    }



    /**
     * 关闭支付单
     *
     * @param closePayOrderRequest 支付单
     * @return 返回信息
     */
    @Override
    public ZingResult closePayOrder(ClosePayOrderRequest closePayOrderRequest) {

        PayOrder payOrder = new PayOrder();
        payOrder.setOrderNo(closePayOrderRequest.getOrderNo());
        payOrder = payOrderService.selectOne(payOrder);

        // 1. 关闭订单前最后一次查询订单状态
        AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder()
                .setOutTradeNo(closePayOrderRequest.getOrderNo());
        AlipayF2FQueryResult result = alipayTradeService.queryTradeResult(builder);

        PayOrder updatePayOrder = new PayOrder();
        updatePayOrder.setId(payOrder.getId());
        if (result.isTradeSuccess()) {
            AlipayTradeQueryResponse response = result.getResponse();
            updatePayOrder.setStatus(OrderStatusEnum.FINISH.getCode());
            updatePayOrder.setTradeTime(new Date());
            updatePayOrder.setOutOrderNo(response.getTradeNo());
            payOrderService.updatePayOrder(updatePayOrder);
            return ZingResult.success();
        }

        updatePayOrder.setStatus(OrderStatusEnum.CANCEL_FAILD.getCode());
        payOrderService.updatePayOrder(updatePayOrder);

        OrderNotify orderNotifyFailed = assemblyOrderNotify(payOrder, updatePayOrder);
        jobService.addNotifyOrderJob(payOrder.getOrderNo(), payOrder.getCallbackUrl(), JSON.toJSONString(orderNotifyFailed));
        return ZingResult.success();
    }

    private OrderNotify assemblyOrderNotify(PayOrder payOrder, PayOrder updatePayOrder) {
        OrderNotify orderNotify = new OrderNotify();
        orderNotify.setBusinessOrderNo(payOrder.getBusinessOrderNo());
        orderNotify.setOrderNo(payOrder.getOrderNo());
        orderNotify.setPayType(payOrder.getPayType());
        orderNotify.setStatus(updatePayOrder.getStatus());
        orderNotify.setOutOrderNo(updatePayOrder.getOutOrderNo());
        orderNotify.setPayAmount(payOrder.getPayAmount());
        orderNotify.setTradeTime(DateUtil.date2StrByPattern(updatePayOrder.getTradeTime(), DateUtil.YYYY_MM_DD_HH_MM_SS));
        return orderNotify;
    }
}
